package com.youhome.service.service.impl;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.youhome.common.config.OSSConfig;
import com.youhome.common.config.ReportOSSConfig;
import com.youhome.common.exception.CustomException;
import com.youhome.common.response.Result;
import com.youhome.common.response.ResultCode;
import com.youhome.service.service.OSSService;
import com.youhome.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Decoder;

import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.List;

/**
 * Created by chen365 on 2019/01/16.
 */
@Service
@Slf4j
public class OSSServiceImpl implements OSSService {
    @Autowired
    private OSSConfig ossConfig;

    private OSSClient getOSSClient() {
        OSSClient ossClient = new OSSClient(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
        return ossClient;
    }

    public AssumeRoleResponse assumeRole(DefaultAcsClient client, String roleArn) throws ClientException {
        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setVersion("2015-04-01");
        request.setMethod(MethodType.POST);
        request.setProtocol(ProtocolType.HTTPS);
        request.setDurationSeconds(3600L);
        request.setRoleArn(roleArn);
        request.setRoleSessionName("video-token");
        return client.getAcsResponse(request);
    }

    public boolean isTimeExpire(String expiration) {
        Date nowDate = new Date();
        Date expireDate = javax.xml.bind.DatatypeConverter.parseDateTime(expiration).getTime();
        if (expireDate.getTime() <= nowDate.getTime()) {
            return true;
        } else {
            return false;
        }
    }

    //@Override
    public AssumeRoleResponse.Credentials getOssToken() throws ClientException {

//        用户登录名称 youhome-video@31181882.onaliyun.com
//        AccessKeyID LTAIIbnagn6Mvi3m
//        AccessKeySecret RwpTWpnWUJzOqY2mgWNxHLkKLMN3iJ

        String accessKeyId = "LTAIIbnagn6Mvi3m";
        String accessKeySecret = "RwpTWpnWUJzOqY2mgWNxHLkKLMN3iJ";
        IClientProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou",
                accessKeyId,
                accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);

        String roleArn = "acs:ram::31181882:role/video";
        AssumeRoleResponse response = assumeRole(client, roleArn);

        AssumeRoleResponse.Credentials credentials = response.getCredentials();

        return credentials;
    }

    @Override
    public boolean delete(String key) {
        try {
            OSSClient ossClient = getOSSClient();
            String bucketName = ossConfig.getBacketName();
            ossClient.deleteObject(bucketName, key);
            ossClient.shutdown();
            log.info("删除" + bucketName + "下的文件" + key + "成功");
            return true;
        } catch (OSSException e) {
            e.printStackTrace();
        } catch (com.aliyun.oss.ClientException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean delete(List<String> keys) {
        try {
            OSSClient ossClient = getOSSClient();
            String backetName = ossConfig.getBacketName();
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(backetName).withKeys(keys));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            ossClient.shutdown();
            log.info("deletedObjects===>>" + deletedObjects);
            log.info("删除" + backetName + "下的文件" + keys + "成功");
            return true;
        } catch (OSSException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 上传文件至OSS
     *
     * @param file 上传文件（文件全路径如：D:\\image\\photos1.jpg）
     * @return String 返回的唯一MD5数字签名
     */
    @Override
    public String upload(File file) {
        OSSClient ossClient = getOSSClient();
        String bucketName = ossConfig.getBacketName();
        String resultStr = null;
        try {
            //以输入流的形式上传文件
            InputStream is = new FileInputStream(file);
            //文件名
            String fileName = file.getName();
            //文件大小
            Long fileSize = file.length();
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(fileName));

            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, fileName, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }

    @Override
    public String upload(String folder, File file) {
        OSSClient ossClient = getOSSClient();
        String bucketName = ossConfig.getBacketName();
        String resultStr = null;
        try {
            //以输入流的形式上传文件
            InputStream is = new FileInputStream(file);
            //文件名
            String fileName = file.getName();
            //文件大小
            Long fileSize = file.length();
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(fileName));

            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, folder + "/" + fileName, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }


    //*****************************************************


    /**
     * 创建存储空间
     *
     * @param ossClient  OSS连接
     * @param bucketName 存储空间
     * @return
     */
    @Override
    public String createBucketName(OSSClient ossClient, String bucketName) {
        //存储空间
        final String bucketNames = bucketName;
        if (!ossClient.doesBucketExist(bucketName)) {
            //创建存储空间
            Bucket bucket = ossClient.createBucket(bucketName);
            log.info("创建存储空间成功");
            return bucket.getName();
        }
        return bucketNames;
    }

    /**
     * 删除存储空间buckName
     *
     * @param ossClient  oss对象
     * @param bucketName 存储空间
     */
    @Override
    public void deleteBucket(OSSClient ossClient, String bucketName) {
        ossClient.deleteBucket(bucketName);
        log.info("删除" + bucketName + "Bucket成功");
    }

    /**
     * 创建模拟文件夹
     *
     * @param ossClient  oss连接
     * @param bucketName 存储空间
     * @param folder     模拟文件夹名如"qj_nanjing/"
     * @return 文件夹名
     */
    @Override
    public String createFolder(OSSClient ossClient, String bucketName, String folder) {
        //文件夹名
        final String keySuffixWithSlash = folder;
        //判断文件夹是否存在，不存在则创建
        if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) {
            //创建文件夹
            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));
            log.info("创建文件夹成功");
            //得到文件夹名
            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);
            String fileDir = object.getKey();
            return fileDir;
        }
        return keySuffixWithSlash;
    }

    @Override
    public void delete(String endpoint, String accessKeyId, String accessKeySecret, String bucketName, String key) {
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        ossClient.deleteObject(bucketName, key);
        ossClient.shutdown();
        log.info("删除" + bucketName + "下的文件" + key + "成功");
    }

    @Override
    public void delete(String endpoint, String accessKeyId, String accessKeySecret, String bucketName, List<String> keys) {
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys));
        List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
        ossClient.shutdown();
        log.info("deletedObjects===>>" + deletedObjects);
        log.info("删除" + bucketName + "下的文件" + keys + "成功");
    }


    /**
     * 上传图片至OSS
     *
     * @param ossClient  oss连接
     * @param file       上传文件（文件全路径如：D:\\image\\photos1.jpg）
     * @param bucketName 存储空间
     * @param folder     模拟文件夹名 如"photos/"
     * @return String 返回的唯一MD5数字签名
     */
    @Override
    public String uploadObject2OSS(OSSClient ossClient, File file, String bucketName, String folder) {
        String resultStr = null;
        try {
            //以输入流的形式上传文件
            InputStream is = new FileInputStream(file);
            //文件名
            String fileName = file.getName();
            //文件大小
            Long fileSize = file.length();
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(fileName));

            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, folder + "/" + fileName, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }


    /**
     * 上传文件至OSS
     *
     * @param ossClient  oss连接
     * @param file       上传文件（文件全路径如：D:\\image\\photos1.jpg）
     * @param bucketName 存储空间
     * @return String 返回的唯一MD5数字签名
     */
    @Override
    public String uploadObject2OSS(OSSClient ossClient, File file, String bucketName) {
        String resultStr = null;
        try {
            //以输入流的形式上传文件
            InputStream is = new FileInputStream(file);
            //文件名
            String fileName = file.getName();
            //文件大小
            Long fileSize = file.length();
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(fileName));
            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, fileName, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }


    /**
     * @param ossClient
     * @param data
     * @param filename
     * @param bucketName
     * @return
     */
    @Override
    public String uploadObject2OSS(OSSClient ossClient, String data, String filename, String bucketName) {
        String resultStr = null;
        try {
            //以输入流的形式上传文件
            InputStream is = new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(data));
            //文件名
            //String fileName = file.getName();
            //文件大小
            //Long fileSize = file.length();
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(filename));
            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            //metadata.setContentDisposition("filename/filesize=" + filename + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, filename, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }

    /**
     * 上传图片至OSS
     *
     * @param ossClient  oss连接
     * @param bytes      上传文件（文件全路径如：D:\\image\\photos1.jpg）
     * @param bucketName 存储空间
     * @param folder     模拟文件夹名 如"photos/"
     * @return String 返回的唯一MD5数字签名
     */
    @Override
    public String uploadObject2OSS(OSSClient ossClient, byte[] bytes, String fileName, String bucketName, String folder) {

        String resultStr = null;
        try {
            InputStream is = new ByteArrayInputStream(bytes);
            //文件大小
            Integer fileSize = bytes.length;
            //创建上传Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上传的文件的长度
            metadata.setContentLength(is.available());
            //指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            //指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            //如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(FileUtils.getContentType(fileName));
            //指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //上传文件   (上传文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, folder + fileName, is, metadata);
            //解析结果
            resultStr = putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return resultStr;
    }

    /**
     * 上传图片至OSS
     *
     * @param bytes      上传文件（文件全路径如：D:\\image\\photos1.jpg）
     * @param bucketName 存储空间
     * @param folder     模拟文件夹名 如"photos/"
     * @return String 返回的唯一MD5数字签名
     */
    @Override
    public String uploadObject2OSS(byte[] bytes, String fileName, String bucketName, String folder) {
        OSSClient ossClient = getOSSClient();
        return uploadObject2OSS(ossClient, bytes, fileName, ossConfig.getBacketName(), "photo/");
    }

    @Override
    public Boolean exist(String endpoint, String accessKeyId, String accessKeySecret, String bucketName, String key) {
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        boolean found = ossClient.doesObjectExist(bucketName, key);
        ossClient.shutdown();
        return found;
    }

    @Override
    public Boolean exist(OSSClient ossClient, String bucketName, String key) {
        boolean found = ossClient.doesObjectExist(bucketName, key);
        ossClient.shutdown();
        return found;
    }

    /**
     * 创建临时访问路径
     *
     * @param ossConfig
     * @param key       文件名
     * @param time      有效时长 ms
     * @return
     */
    @Override
    public String createTemporaryUrl(OSSConfig ossConfig, String key, Long time) {
        OSSClient client = new OSSClient(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
        // 设置URL过期时间
        Date expiration = new Date(new Date().getTime() + time);
        // 生成URL
        URL url = client.generatePresignedUrl(ossConfig.getBacketName(), key, expiration);
        //System.err.println(url.toString().replace(ossConfig.getEndpoint(),""));
        client.shutdown();
        return url.toString().replace("http://youhome.oss-cn-zhangjiakou-internal.aliyuncs.com", "http://youhome.oss-cn-zhangjiakou.aliyuncs.com");
    }

    /**
     * 创建临时访问路径
     *
     * @param ossConfig
     * @param key       文件名
     * @param time      有效时长 ms
     * @return
     */
    @Override
    public String createTemporaryUrl(ReportOSSConfig ossConfig, String key, Long time) {
        OSSClient client = new OSSClient(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
        // 设置URL过期时间
        Date expiration = new Date(new Date().getTime() + time);
        // 生成URL
        URL url = client.generatePresignedUrl(ossConfig.getBacketName(), key, expiration);
        //System.err.println(url.toString().replace(ossConfig.getEndpoint(),""));
        client.shutdown();                 //http://youhome-report.oss-cn-zhangjiakou.aliyuncs.com
        return url.toString().replace("-internal", "");
    }

    /**
     * 创建临时访问路径
     *
     * @param client
     * @param backetName
     * @param key
     * @param time
     * @return
     */
    @Override
    public String createTemporaryUrl(OSSClient client, String backetName, String key, Long time) {

        // 设置URL过期时间
        Date expiration = new Date(new Date().getTime() + time);
        // 生成URL
        URL url = client.generatePresignedUrl(backetName, key, expiration);
        client.shutdown();
        return url.toString();
    }

    /**
     * 下载json
     *
     * @return
     */
    @Override
    public String downloadJson(OSSClient ossClient, String bucketName, String key) {
        boolean found = ossClient.doesObjectExist(bucketName, key);
        StringBuilder sb = new StringBuilder();
        try {
            if (found) {
                OSSObject ossObject = ossClient.getObject(bucketName, key);
                BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
                while (true) {
                    String line = reader.readLine();
                    if (line == null)
                        break;
                    sb.append(line);
                }
                //数据读取完成后，获取的流一定要显示close，否则会造成资源泄露
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (OSSException e) {
            e.printStackTrace();
        } finally {
            // 关闭client
            ossClient.shutdown();
        }
        if (sb.length() > 0) {
            return sb.toString();
        } else {
            return null;
        }
    }

    @Override
    public void delete(OSSClient ossClient, String bucketName, String key) {
        try {
            ossClient.deleteObject(bucketName, key);
            ossClient.shutdown();
            log.info("删除" + bucketName + "下的文件" + key + "成功");
        } catch (OSSException e) {
            e.printStackTrace();
            log.info("删除" + bucketName + "下的文件" + key + "失败");
        } catch (com.aliyun.oss.ClientException e) {
            e.printStackTrace();
            log.info("删除" + bucketName + "下的文件" + key + "失败");
        }
    }

    @Override
    public AssumeRoleResponse.Credentials getVideoToken() throws CustomException {
        try {
            return getOssToken();
        } catch (ClientException e) {
            e.printStackTrace();
            throw new CustomException(ResultCode.FAIL);
        }
    }

}
